package edu.pku.sei.modeler.uml.obsolete.fromdiagram;

import org.eclipse.gef.commands.Command;

import edu.pku.sei.modeler.commands.ModelCommand;
import edu.pku.sei.modeler.exceptions.FactoryNotFoundException;
import edu.pku.sei.modeler.model.common.ModelRegistry;
import edu.pku.sei.modeler.model.core.MetaModel;
import edu.pku.sei.modeler.model.core.MetaModelElement;
import edu.pku.sei.modeler.model.structure.MetaBendPoint;
import edu.pku.sei.modeler.model.structure.MetaLink;
import edu.pku.sei.modeler.model.structure.MetaNode;
import edu.pku.sei.modeler.model.structure.StructureFactory;
import edu.pku.sei.modeler.model.uml.Kernel.Association;
import edu.pku.sei.modeler.model.uml.Kernel.Classifier;
import edu.pku.sei.modeler.model.uml.Kernel.Generalization;
import edu.pku.sei.modeler.model.uml.Kernel.IClassifier;
import edu.pku.sei.modeler.model.uml.Kernel.IType;
import edu.pku.sei.modeler.model.uml.Kernel.Property;
import edu.pku.sei.modeler.model.uml.UseCases.*;
import edu.pku.sei.modeler.uml.modelHelper.ModelFactory;

public class UMLCreateLinkCommand extends Command {
	private MetaLink link;

	private MetaNode source, target;

	private ModelCommand modelCmd;

	public UMLCreateLinkCommand(MetaLink newObj, MetaNode src) {
		
		super("create a link");
		link = newObj;
		source = src;
		MetaModelElement modelElement = link.getModelElement();
		MetaModelElement sourceModelElement = src.getModelElement();
		if (modelElement instanceof Include) {
			modelCmd = new CreateIncludeCommand((Include) modelElement, sourceModelElement);
		} else if (modelElement instanceof Association) {
			modelCmd = new CreateAssociationCommand(modelElement, sourceModelElement);
			
		}else if (modelElement instanceof Generalization) {
			modelCmd = new CreateGeneralizationCommand(modelElement,sourceModelElement);
		}else if (modelElement instanceof Extend) {
			modelCmd = new CreateExtendCommand(modelElement,sourceModelElement);
		}
	}

	public UMLCreateLinkCommand(MetaLink newObj, MetaNode src, ModelCommand cmd) {
		this.link = newObj;
		this.source = src;
		this.modelCmd = cmd;
	}

	@Override
	public boolean canExecute() {
		boolean result = source != null && target != null && link != null;
		if (modelCmd != null)
			result &= modelCmd.canExecute();
		return result;
	}

	@Override
	public void execute() {
		if (source == target) {
			StructureFactory sFactory;
			try {
				sFactory = (StructureFactory) ModelRegistry.getRegistry()
						.getStructureFactory(source.getModelElement().getModel());
				MetaBendPoint p = sFactory.createMetaBendPoint();
				MetaBendPoint p1 = sFactory.createMetaBendPoint();
				MetaBendPoint p2 = sFactory.createMetaBendPoint();
				MetaBendPoint p3 = sFactory.createMetaBendPoint();
				p.x = source.getLocationX();
				p.y = source.getLocationY();

				p1.x = p.x - 10;
				p1.y = p.y + 10;
				p2.x = p.x - 10;
				p2.y = p.y - 10;
				p3.x = p.x + 10;
				p3.y = p.y - 10;

				link.getBendPoints().add(p1);
				link.getBendPoints().add(p2);
				link.getBendPoints().add(p3);
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (FactoryNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if (modelCmd != null && !link.isReferenceShape())
			modelCmd.execute();

		// FIXME: This statement has an non-local-handled exception
		link.setSource(source);
		source.getOutgoings().add(link);
		link.setTarget(target);
		target.getIncomings().add(link);
		link.setModel(source.getModel());
		// source.getDiagram().getConnections().add(link);
	}

	public void setTarget(MetaNode target) {
		this.target = target;
		if (modelCmd != null) {
			modelCmd.setTarget(target.getModelElement());
		}
	}

	@Override
	public void undo() {
		link.setSource(null);
		link.setTarget(null);
		if (source == target) {
			link.getBendPoints().clear();
		}
		if (modelCmd != null)
			modelCmd.undo();
	}

	private class CreateIncludeCommand extends ModelCommand {
		private UseCase src, des;

		private Include include;

		public CreateIncludeCommand(Include include, Object src) {
			this.include = include;

			if (src instanceof UseCase) {
				this.src = (UseCase) src;
			}

		}

		@Override
		public void setTarget(Object des) {
			if (des instanceof UseCase)
				this.des = (UseCase) des;
		}

		public boolean canExecute() {
			return true;
		}

		public void execute() {
			if (src != null)
				src.getInclude().add(include);
			if (include != null) {
				include.setContainer(src);
				include.setAddition(des);
				include.setModel(src.getModel());
			}
		}

		public void undo() {

		}

	}

	private class CreateAssociationCommand extends ModelCommand {
		private IType src;

		private IType des;

		private Association ref;

		public CreateAssociationCommand(Object ref, Object src) {
			this.ref = (Association) ref;
			if (src instanceof IType)
				this.src = (IType) src;
		}

		public void setTarget(Object des) {
			if (des instanceof IType)
				this.des = (IType) des;
		}

		public boolean canExecute() {
			return true;
		}

		public void execute() {
			if (src == null) {
				return;
			}
			
			
			if (! (src instanceof IClassifier)) {
				return;
			}
			
			Classifier sourceClassifier = (Classifier)src;
			 
			try {
				MetaModel model = sourceClassifier.getModel();
				Property property = ((ModelFactory) ModelRegistry.getRegistry().getFactory(model)).createProperty();
				property.setModel(model);
				property.getAssociation().add(ref);
				sourceClassifier.getAttribute().add(property);
				if (ref != null) {
					ref.setContainer(sourceClassifier);
					ref.setModel(model);
				}
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (FactoryNotFoundException e) {
				e.printStackTrace();
			}
			
			if (src instanceof IActor) {
				((IActor)src).getOwnedUseCase().add(des);
			}
			
//			if (src != null)
//				src.getOwnedUseCase().add(des);
		}

		public void undo() {

		}
	}
	
//	private class CreateClassAssociationCommand extends ModelCommand {
//		private UMLClass src;
//
//		private UMLClass des;
//
//		private Association ref;
//
//		public CreateClassAssociationCommand(Object ref, Object src) {
//			this.ref = (Association) ref;
//			if (src instanceof UMLClass)
//				this.src = (UMLClass) src;
//		}
//
//		public void setTarget(Object des) {
//			if (des instanceof UMLClass)
//				this.des = (UMLClass) des;
//		}
//
//		public boolean canExecute() {
//			return true;
//		}
//
//		public void execute() {
//			if (src != null)
//				src.get
//			Property property;
//			try {
//				property = ((ModelFactory) ModelRegistry.getRegistry().getFactory(src.getModel())).createProperty();
//				property.getassociation().add(ref);
//				src.getAttribute().add(property);
//				if (ref != null) {
//					ref.setContainer(src);
//					ref.setModel(src.getModel());
//				}
//			} catch (IllegalArgumentException e) {
//				e.printStackTrace();
//			} catch (FactoryNotFoundException e) {
//				e.printStackTrace();
//			}
//		}
//
//		public void undo() {
//
//		}
//	}
	
	private class CreateExtendCommand extends ModelCommand {
		private UseCase src,des;
		private Extend extend;
		
		public CreateExtendCommand(Object extend, Object src) {
			this.extend = (Extend)extend;
			if (src instanceof UseCase) {
				this.src = (UseCase)src;
			}
		}
		

		@Override
		public void setTarget(Object target) {
			if (target instanceof UseCase) {
				this.des = (UseCase)target;
			}
			
		}
		
		public boolean canExecute() {
			return des != null && des.getExtensionPoint().size() > 0;
		}
		
		public void execute() {
			if(src != null)
				src.getExtend().add(extend);
			if(extend != null) {
				extend.setContainer(src);
				extend.setExtendedCase(des);
				extend.setModel(src.getModel());
			
			}
		}
		
		public void undo() {
			
		}
		
	}
	
	private class CreateGeneralizationCommand extends ModelCommand
	{
		private Classifier src, des;
		private Generalization gen;
		
		public CreateGeneralizationCommand(Object gen, Object src) {
			this.gen = (Generalization)gen;
			if(src instanceof Classifier)
				this.src = (Classifier)src;
		}
		
		public void setTarget(Object des) {
			if(des instanceof Classifier)
				this.des = (Classifier)des;
		}
		
		public boolean canExecute() {
			return true;
		}
		
		public void execute() {
			if(src != null)
				src.getGeneralization().add(gen);
				src.getGeneral().add(des);
			if(gen != null) {
				gen.setContainer(src);
				
				gen.setModel(src.getModel());
			}
		}
		
		public void undo() {
			
		}
	} 
}
